package com.yzy.baselibrary.base

import android.app.ActivityManager
import android.app.Application
import android.content.Context
import android.os.Process
import android.util.Log
import androidx.multidex.MultiDex
import com.yzy.baselibrary.BuildConfig
import com.yzy.baselibrary.di.*
import com.yzy.baselibrary.integration.*
import com.blankj.utilcode.util.LogUtils
import com.blankj.utilcode.util.Utils
import com.tencent.mmkv.MMKV
import org.kodein.di.Kodein
import org.kodein.di.KodeinAware
import org.kodein.di.android.support.androidSupportModule
import org.kodein.di.generic.bind
import org.kodein.di.generic.instance
import org.kodein.di.generic.singleton
import java.util.*
import java.util.concurrent.Executors

/**
 *description: CommApplication.
 *@date 2018/10/12 15:55.
 *@author: YinZeYu.
 */
open class BaseApplication : Application(), KodeinAware {
  private val mLifecycles = ArrayList<AppLifecycle>()
  private var configModules: List<ConfigModule>? = null

  val appManager: AppManager by instance()

  private val repositoryManager: IRepositoryManager by instance()

  var launcherTime = 0L

  //初始化全局的Kodein
  final override val kodein: Kodein = Kodein.lazy {
    bind<Context>() with singleton { this@BaseApplication }
    import(androidSupportModule(this@BaseApplication))

    import(imageLoaderModule)
    configModules = getModuleConfig()
    configModules?.let {
      import(getGlobeConfigModule(it).globeConfigModule)
    }
    import(appModule)
    import(ClientModule.clientModule)
  }


  override fun onCreate() {
    super.onCreate()
    INSTANCE = this
    var time = System.currentTimeMillis()
    //MMKV初始化
    MMKV.initialize(this)
    addInitInfo("【MMKV耗时=${(System.currentTimeMillis() - time)}】")
    if (isMainProcess()) {
      time = System.currentTimeMillis()
      registerActivityLifecycleCallbacks(ActivityLifecycle(appManager))
      addInitInfo("【Lifecycle生命周期耗时=${System.currentTimeMillis() - time}】")
      time = System.currentTimeMillis()
      injectConfigModule(configModules)
      addInitInfo("【Config注入耗时=${System.currentTimeMillis() - time}】")
      time = System.currentTimeMillis()
      initThirdPart()
      addInitInfo("【三方初始化耗时=${System.currentTimeMillis() - time}】")
      if (useThreadInit) {
        cachedThreadPool.execute {
          val timePool = System.currentTimeMillis()
          addTimeInit(timePool)
          mLifecycles.forEach {
            it.onCreate(this@BaseApplication)
          }
          removeTimeInit(timePool)
          addInitInfo("【ThreadPool:统计页面时长耗时=${(System.currentTimeMillis() - timePool)}】")
        }
      } else {
        time = System.currentTimeMillis()
        mLifecycles.forEach {
          it.onCreate(this@BaseApplication)
        }
        addInitInfo("【统计页面时长耗时=${(System.currentTimeMillis() - time)}】")
      }
      time = System.currentTimeMillis()
      initAppBackgroundManager()
      addInitInfo("【前后台切换管理耗时=${System.currentTimeMillis() - time}】")
      this.initInMainProcess()
    }
  }

  //App前后台切换的管理
  private fun initAppBackgroundManager() {
    AppBackgroundManager.setAppStateListener(object : AppBackgroundManager.IAppStateChangeListener {
      override fun onAppStateChanged(isAppForceground: Boolean) {
        mLifecycles.forEach {
          if (isAppForceground) {
            it.onFront()
          } else {
            it.onBackgroud()
          }
        }
      }
    })
  }

  /**
   * 获取模块的配置
   */
  private fun getModuleConfig(): List<ConfigModule> =
      ManifestParser(this@BaseApplication).parse()

  /**
   * 将app的全局配置信息封装进module(使用Dagger注入到需要配置信息的地方)
   * 需要在AndroidManifest中声明[ConfigModule]的实现类,和Glide的配置方式相似
   */
  private fun getGlobeConfigModule(modules: List<ConfigModule>): GlobeConfigModule {
    val builder = GlobeConfigModule
        .builder()
        .baseUrl("https://api.github.com")//为了防止用户没有通过GlobeConfigModule配置baseurl,而导致报错,所以提前配置个默认baseurl
    for (module in modules) {
      module.applyOptions(this, builder)
    }
    return builder.build()
  }

  private fun injectConfigModule(modules: List<ConfigModule>?) {
    modules?.forEach {
      it.injectAppLifecycle(this, mLifecycles)
      it.registerComponents(this@BaseApplication, repositoryManager)
    }
  }

  /**
   * 初始化第三方的一些东西
   */
  private fun initThirdPart() {
    //Utils
    Utils.init(this)
    LogUtils.getConfig()
        .setLogSwitch(BuildConfig.DEBUG)//log开关
        .setGlobalTag("Aimy")//全局标签
        .setStackDeep(3)//log栈
  }


  override fun attachBaseContext(base: Context) {
    launcherTime = System.currentTimeMillis()
    super.attachBaseContext(base)
    val time = System.currentTimeMillis()
    MultiDex.install(this)
    addInitInfo("【MultiDex耗时=${System.currentTimeMillis() - time}】")
  }

  /**
   * 程序终止的时候执行
   */
  override fun onTerminate() {
    super.onTerminate()
    mLifecycles.forEach {
      it.onTerminate(this@BaseApplication)
    }
  }

  /**
   * 是否主进程
   */
  private fun isMainProcess(): Boolean {
    val am = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
    if (am.runningAppProcesses == null) {
      return false
    }
    val processInfo = am.runningAppProcesses
    val mainProcessName = packageName
    val myPid = Process.myPid()
    for (info in processInfo) {
      if (info.pid == myPid && mainProcessName == info.processName) {
        return true
      }
    }
    return false
  }


  companion object {
    lateinit var INSTANCE: BaseApplication
  }

  /**
   * 在主进程中进行操作
   */
  protected open fun initInMainProcess() {

  }

  //---------------------------优化启动速度添加的内容2019年3月20日 by case---------------------------//
  //判断是否全部初始化完成
  val longArray = mutableListOf<Long>()
  //统计初始化时间
  private val stringBuffer = StringBuffer()
  //线程池
  val cachedThreadPool = Executors.newCachedThreadPool()
  //是否开启异步初始化
  val useThreadInit =
      true
  //是否是冷启动
  var isColdStart = true

  //添加执行开始时间
  fun addTimeInit(time: Long) {
    if (isMainProcess()) {
      longArray.add(time)
    }
  }

  //移除执行时间
  fun removeTimeInit(time: Long) {
    if (isMainProcess()) {
      longArray.remove(time)
    }
  }

  //是否初始化完成
  fun isInitFinish(): Boolean {
    return longArray.size == 0
  }

  //添加初始化信息
  fun addInitInfo(info: String, first: Boolean = false) {
    if (isMainProcess()) {
      Log.i("addInitInfo", info)
      if (first) {
        stringBuffer.insert(0, info)
      } else {
        stringBuffer.append(info)
      }
    }
  }

  //获取统计信息
  fun getInitInfo(): String {
    return if (stringBuffer.isEmpty()) "" else stringBuffer.toString()
  }
}
